#include "filestream.h"
#include <QTextCodec>
#include <QDebug>

FileStream *FileStream::m_this = NULL;

FileStream::FileStream(const QString dllPath, QObject *parent)
    : QObject(parent), m_handle(nullptr)
{
    m_this = this;
    if (dllPath.isEmpty()) return;
    loadDllLibrary(dllPath);
}

FileStream::~FileStream()
{
    closeLibrary();
}

bool FileStream::loadDllLibrary(const QString dllPath)
{
    StreamApi *api;

    // qDebug() << __func__ << "(" << __LINE__ << ")" << Qt::endl;
    closeLibrary();
    m_handle = LoadLibraryA(dllPath.toLocal8Bit().data());
    if (m_handle == nullptr) return false;

    // qDebug() << __func__ << "(" << __LINE__ << ")" << Qt::endl;
    m_inOut = (InOutStreamType*) GetProcAddress(m_handle, "InOutStream");
    if (m_inOut == nullptr) goto err;

    // qDebug() << __func__ << "(" << __LINE__ << ")" << Qt::endl;
    m_pauseAll = (PauseAllFunc) GetProcAddress(m_handle, "PauseAll");
    if (m_pauseAll == nullptr) goto err;

    // qDebug() << __func__ << "(" << __LINE__ << ")" << Qt::endl;
    api = (StreamApi*) GetProcAddress(m_handle, "streamApi");
    if (api == nullptr) goto err;

    // qDebug() << __func__ << "(" << __LINE__ << ")" << Qt::endl;
    FreeAllBuffer = api->FreeAllBuffer;
    api->ThrowError = ThrowError;
    api->Log = LogOut;
    api->PrintLevelAdd = PrintLevelAdd;
    api->PrintLevelSub = PrintLevelSub;
    api->PrintLabelKey = PrintLabelKey;
    api->PrintLabelValue = PrintLabelValue;
    api->PrintLabelEndKey = PrintLabelEndKey;

    return true;
err:
    closeLibrary();
    return false;
}

void FileStream::closeLibrary()
{
    if (m_handle == nullptr) return;
    FreeLibrary(m_handle);
    m_handle = nullptr;
}

void FileStream::setData(QByteArray &data)
{
    if (m_handle == nullptr) return;
    m_inOut->buf = (unsigned char*) data.data();
    m_inOut->len = data.length();
    m_inOut->pos = 0;
    m_inOut->bit = 0;
}

bool FileStream::runPauseDll(TreeItem *root)
{
    if (m_handle == nullptr) return false;
    if (root == nullptr) return false;
    if (m_inOut->buf == nullptr) return false;

    int ret = setjmp(m_abnormalEnv);
    if (ret != 0) {
        if (FreeAllBuffer != nullptr) FreeAllBuffer();
        emit onError(ret);
        return false;
    }

    m_prtTree = root;
    m_curTree = root;
    m_treeStrack.clear();
    m_pauseAll(nullptr, 0);
    if (FreeAllBuffer != nullptr) FreeAllBuffer();
    return true;
}

QString FileStream::getSupportFileFilter(const QString dllPath)
{
    QString str;
    // qDebug() << __func__ << "(" << __LINE__ << ")" << Qt::endl;
    HMODULE handle = LoadLibraryA(dllPath.toLocal8Bit().data());
    if (handle == nullptr) return str;
    const char *filter = (char*) GetProcAddress(handle, "SupportFileFilter");
    // qDebug() << __func__ << " " << filter << Qt::endl;
    if (filter != nullptr && filter[0] != '\0') {
        str = tr(filter);
    }
    FreeLibrary(handle);
    return str;
}

void FileStream::ThrowError(int errCode)
{
    longjmp(m_this->m_abnormalEnv, errCode);
}

void FileStream::LogOut(int type, const char *format, va_list arg)
{
    char buffer[1024];
    vsnprintf(buffer, sizeof(buffer), format, arg);
    QTextCodec* gbk = QTextCodec::codecForName("GB18030");
    emit m_this->onLog(type, gbk->toUnicode(buffer));
}

void FileStream::PrintLevelAdd(int type)
{
    if (type != T_Print) return;
    m_this->m_treeStrack.push(m_this->m_prtTree);
    m_this->m_prtTree = m_this->m_curTree;
}

void FileStream::PrintLevelSub(int type)
{
    if (type != T_Print) return;
    m_this->m_curTree = m_this->m_prtTree;
    m_this->m_prtTree = m_this->m_treeStrack.pop();
}

void FileStream::PrintLabelKey(const char *key, int type)
{
    if (type != T_Print) return;
    m_this->m_curTree = new TreeItem;
    m_this->m_curTree->dataPos = m_this->m_inOut->pos;
    m_this->m_curTree->setText(0, QString("%1\t:").arg(key));
    m_this->m_prtTree->addChild(m_this->m_curTree);
}

void FileStream::PrintLabelValue(const char *val, int type)
{
    if (type != T_Print) return;
    QTextCodec* gbk = QTextCodec::codecForName("GB18030");
    QString str = m_this->m_curTree->text(0);
    str += gbk->toUnicode(val);
    m_this->m_curTree->setText(0, str);
}

void FileStream::PrintLabelEndKey(int type)
{
    if (type != T_Print) return;
    m_this->m_curTree->dataLen = m_this->m_inOut->pos - m_this->m_curTree->dataPos;
    if (m_this->m_inOut->bit > 0) {
        m_this->m_curTree->dataLen++;
    }
    if (m_this->m_curTree->dataLen <= 0) {
        if (m_this->m_inOut->size != 0) {
            m_this->m_curTree->dataLen = m_this->m_inOut->size / 8;
        } else {
            m_this->m_prtTree->removeChild(m_this->m_curTree);
        }
    }
}
